#include "preview.h"

preview::preview(QWidget *parent, 
	QGLWidget *shareWidget, 
	QString &fileName, 
	gObjectContainer *container,
	gObjectContainer *buffContainer
	): QGLWidget(parent, shareWidget), buffCont(buffContainer){
	
	setFocusPolicy(Qt::TabFocus);
	showNormal = showBuff = showAxis = showPoints = true;
	listNormal = listBuff = 0;
	timer = new QTimer(this);
	connect(timer, SIGNAL(timeout()), this, SLOT(timeout()));
	timer->start(10);
	setMouseTracking(true);
	click = false;
	initObject(fileName, container);
	
}

void preview::initObject(QString &path, gObjectContainer *container){
	
	std::ifstream readFile(path.toLatin1(), std::ios_base::binary);
	std::stringstream temp(std::ios::out | std::ios::in | std::ios::binary);
	gObjectContainer buffContainer;
	
	if(!readFile)
		return;
	
	serialize serial(&temp, &readFile);
	
	serial.setOffsets(false);
	try{
		serial.writeGContainer(*container);
	}
	catch(std::ifstream::failure &e){
		return;
	}

	reader read(&temp);
	lexical lex(&read);
	syntax syn(&lex);
	QList<float> buff;
	
	read.initialize();
	syn.getAllCoords(object);
	buffContainer.push_back(&object);
	for(unsigned int i = 0; i < buffCont->size(); i++)
		buffContainer.push_back(buffCont->at(i));
	getRect(buff, buffContainer);
	getMiddle(buff, middle);
	a = longestEdge(buff) * 1.8;
	
}

void preview::timeout(){

	updateGL();
	
}

void preview::initializeGL(){
	
	glClearColor(0.0f, 0.0f, 0.0f, 0.0f);       
	glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
	glDisable(GL_CULL_FACE);
	if(object.size() > 1){
		listNormal = glGenLists(1);
		listBuff = glGenLists(1);
		glNewList(listNormal, GL_COMPILE);
		drawObject(object, NORMAL);
		glEndList();
		glNewList(listBuff, GL_COMPILE);
		drawContainer(*buffCont, BUFF);
		glEndList();
	}

}

void preview::resizeGL(int w, int h){

	int width, height;

	width = height = 0;
	if(w > h)
		width = a * (w / (float) h - 1);
	else
		height = a * (h / (float) w - 1);
	glViewport(0, 0, w, h);
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	glOrtho(
		middle[0] - (a / 2 + BORDER), 
		middle[0] + (a / 2 + BORDER) + width,
		middle[1] - (a / 2 + BORDER) - height, 
		middle[1] + (a / 2 + BORDER), 
        2000, -2000);

}

void preview::mousePressEvent(QMouseEvent *event){

	click = true;
	clicked = event->posF();

}

void preview::mouseReleaseEvent(QMouseEvent *event){

	click = false;
	clickedOld = angle;

}

void preview::mouseMoveEvent(QMouseEvent *event){

	if(click){	
		angle.setX(clickedOld.x() + clicked.x() - event->x());
		angle.setY(clickedOld.y() + clicked.y() - event->y());
	}
	updateGL();

}

void preview::fixRotatePoint(){

	glTranslatef(middle[0], middle[1], middle[2]);
	glRotatef(angle.y(), 1.0f, 0.0f, 0.0f);
    glRotatef(angle.x(), 0.0f, 1.0f, 0.0f);
	glTranslatef(-middle[0], -middle[1], -middle[2]);

}

void preview::initCamera(){
	
	glClear(GL_COLOR_BUFFER_BIT);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
	gluLookAt(
        0, 0, 10,
		0, 0, 0,
		0.0, 1.0, 0.0
		);

}

void preview::drawBegin(gTypeGo &g){

	glTranslatef(g[0], g[1], g[2]);
	
	glColor3f(1.0, 0.0, 0.0);
	GLUquadric *quadric = gluNewQuadric();
	gluQuadricDrawStyle(quadric, GLU_FILL);
	gluQuadricNormals(quadric, GLU_SMOOTH);
	gluSphere(quadric, a * 0.01, 20, 20);
	gluDeleteQuadric(quadric);
	
	glTranslatef(-g[0], -g[1], -g[2]);
	
}

void preview::drawEnd(gTypeGo &g){

	glTranslatef(g[0], g[1], g[2]);
	
	glColor3f(1.0, 1.0, 0.0);
	GLUquadric *quadric = gluNewQuadric();
	gluQuadricDrawStyle(quadric, GLU_FILL);
	gluQuadricNormals(quadric, GLU_SMOOTH);
	gluCylinder(quadric, a * 0.01, 0, a * 0.015, 20, 20);
	gluDeleteQuadric(quadric);
	
	glTranslatef(-g[0], -g[1], -g[2]);

}

void preview::drawObject(gObject &object, types type){

	gObject::iterator iter = object.begin();
	gTypeGo *go, *prev;
	
	go = dynamic_cast<gTypeGo *>(*iter);
	for(iter++; iter != object.end(); iter++){
		prev = go;
		go = dynamic_cast<gTypeGo *>(*iter);
		if(type == BUFF)
			glColor3f(0.0, 1.0, 0.0);
		else if(type == NORMAL)
			glColor3f(1.0, 1.0, 1.0);
		switch(go->getTypeValue()){
			case TYPE_LINEAR_CUT:
				drawLine(*prev, *go);
				break;
			case TYPE_LINEAR_MOVE:
				if(type == NORMAL)
					glColor3f(0.0, 0.0, 1.0);
				drawLine(*prev, *go);
				break;
			case TYPE_ARC_CLOCK:
				drawCircle(*prev, dynamic_cast<gTypeGoCircle &>(*go));
				break;
			case TYPE_ARC_COUNTER:
				drawCircle(*prev, dynamic_cast<gTypeGoCircle &>(*go));
				break;
		}
	}

}

void preview::drawContainer(gObjectContainer &container, types type){
	
	gObjectContainer::iterator iter = container.begin();
	
	for(; iter != container.end(); iter++)
		drawObject(**iter, type);
	
}

void preview::paintGL(){

	initCamera();
	fixRotatePoint();
	
	if(showNormal)
		glCallList(listNormal);
	if(showBuff)
		glCallList(listBuff);
	if(showAxis)
		drawAxis();
	if(showPoints){
		drawBegin(dynamic_cast<gTypeGo &>(*object[0]));
		gTypeGo &g = dynamic_cast<gTypeGo &>(*object[object.size() - 1]);
		drawEnd(g);
	}
	
}

void preview::drawLine(gTypeGo &prev, gTypeGo &go){

	glBegin(GL_LINES);
		glVertex3f(prev[0], prev[1], prev[2]);
		glVertex3f(go[0], go[1], go[2]);
	glEnd();

}

void preview::swap(float *a, float *b) const{

	float buff;
	
	buff = *a;
	*a = *b;
	*b = buff;

}

void preview::getRect(QList<float> &list, gObject &object) const{

	gObject::iterator iter = object.begin();
	float minX, minY, minZ, maxX, maxY, maxZ;
	
	gTypeGo &go = dynamic_cast<gTypeGo &>(**iter);
	minX = maxX = go[0];
	minY = maxY = go[1];
	minY = maxZ = go[2];
	for(iter++; iter != object.end(); iter++){
		gTypeGo &go = dynamic_cast<gTypeGo &>(**iter);
		if(minX > go[0])
			minX = go[0];
		if(maxX < go[0])
			maxX = go[0];
		if(minY > go[1])
			minY = go[1];
		if(maxY < go[1])
			maxY = go[1];
		if(minZ > go[2])
			minZ = go[2];
		if(maxZ < go[2])
			maxZ = go[2];
	}
	list.append(minX);//0
	list.append(maxY);//1
	list.append(maxZ);//2
	list.append(maxX);//3
	list.append(minY);//4
	list.append(minZ);//5
	
}

void preview::getRect(QList<float> &list, gObjectContainer &container) const{

	gObjectContainer::iterator iter = container.begin();
	QList<float> buff;
	
	getRect(list, **iter);
	for(iter++; iter != container.end(); iter++){
		getRect(buff, **iter);
		if(list[0] > buff[0])
			list[0] = buff[0];
		else if(list[3] < buff[3])
			list[3] = buff[3];
		if(list[4] > buff[4])
			list[4] = buff[4];
		else if(list[1] < buff[1])
			list[1] = buff[1];
		if(list[5] > buff[5])
			list[5] = buff[5];
		else if(list[2] < buff[2])
			list[2] = buff[2];
		buff.clear();
	}

}

void preview::getMiddle(const QList<float> &list, QList<float> &middle) const{

	middle.append((list[3] + list[0]) / 2);
	middle.append((list[1] + list[4]) / 2);
	middle.append((list[5] + list[2]) / 2);

}

float preview::longestEdge(const QList<float> &list) const{

	float x, y, z;
	
	x = fabs(list[3] - list[0]);
	y = fabs(list[1] - list[4]);
	z = fabs(list[2] - list[5]);
	
	if(x > y){
		if(x > z)
			return x;
		else
			return z;
	}
	if(y > z)
		return y;
	return z;

}

void preview::drawCircle(gTypeGo &prev, gTypeGoCircle &go){

	float angles[2], s[2], inc, r;
	
	s[0] = prev[0] + go[3];
	s[1] = prev[1] + go[4];
	angles[0] = atan2(prev[1] - s[1], prev[0] - s[0]);
	angles[1] = atan2(go[1] - s[1], go[0] - s[0]);
	
	if(go.getTypeValue() == TYPE_ARC_CLOCK)
		swap(&angles[0], &angles[1]);
	
	r = sqrt((go[0] - s[0]) * (go[0] - s[0]) + (go[1] - s[1]) * (go[1] - s[1]));
	inc = asin(INC / (2 * r)) / 2;
	
	glBegin(GL_LINE_STRIP);
		while(fabs(angles[0] - angles[1]) > inc){	
			glVertex3f(s[0] + r * cos(angles[0]), s[1] + r * sin(angles[0]), go[2]);
			angles[0] += inc;
			if(angles[0] > M_PI)
				angles[0] -= 2 * M_PI;
		}
	glVertex3f(s[0] + r * cos(angles[1]), s[1] + r * sin(angles[1]), go[2]);
	glEnd();

}

void preview::drawAxis(){

	glEnable(GL_LINE_STIPPLE);
	glLineStipple(1, 0x3c3c);
	glColor3f(0.5, 0.5, 0.5);
	glBegin(GL_LINES);
		glVertex3f(middle[0] - a, 0.0, 0.0);
		glVertex3f(middle[0] + a, 0.0, 0.0);
		glVertex3f(0.0, middle[0] - a, 0.0);
		glVertex3f(0.0, middle[0] + a, 0.0);
		glVertex3f(0.0, 0.0, middle[0] - a);
		glVertex3f(0.0, 0.0, middle[0] + a);
	glEnd();
	glDisable(GL_LINE_STIPPLE);

}

void preview::setFile(QString &fileName){

	std::ifstream file(fileName.toLatin1());
	reader read(&file);
	lexical lex(&read);
	syntax syn(&lex);
	QList<float> buff;
	
	read.initialize();
	syn.getAllCoords(object);
	getRect(buff, object);
	getMiddle(buff, middle);
	a = longestEdge(buff) * sqrt(3.0);

}

void preview::keyPressEvent(QKeyEvent *event){

	if(event->modifiers() == Qt::ControlModifier){
		switch(event->key()){
			case Qt::Key_A:
				showAxis = showAxis ? false : true;
				break;
			case Qt::Key_P:
				showPoints = showPoints ? false : true;
				break;
			case Qt::Key_T:
				showBuff = showBuff ? false : true;
				break;
		}
		paintGL();
	}

}
